package com.borened.common.boot.exception;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.borened.common.core.base.result.R;
import com.borened.common.core.base.result.ResultCode;
import com.borened.common.core.constant.EnvConstants;
import com.borened.common.core.exception.BusinessException;
import com.borened.common.core.exception.TokenParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;

import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 微服务全局异常处理器，该类只处理一般异常，对于业务模块异常可再细化实现。
 *
 * @author: cch
 * @since: 2021/11/15
 * @see  ExceptionHandlerExceptionResolver#getExceptionHandlerMethod
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    @Value("${spring.profiles.active}")
    private String env;

    /*
     * ------------------400错误----------------
     */


    /**
     * 表单绑定到 java bean 出错时抛出 BindException 异常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(BindException.class)
    public <T> R<T> processException(BindException e) {
        log.error("参数绑定异常: {}", ExceptionUtil.getMessage(e));
        JSONObject msg = new JSONObject();
        e.getAllErrors().forEach(error -> {
            if (error instanceof FieldError) {
                FieldError fieldError = (FieldError) error;
                msg.set(fieldError.getField(),
                        fieldError.getDefaultMessage());
            } else {
                msg.set(error.getObjectName(),
                        error.getDefaultMessage());
            }
        });
        return R.fail(ResultCode.REQUEST_PARAM_ERROR, msg.toString());
    }

    /**
     * 普通参数(非 java bean)校验出错时抛出 ConstraintViolationException 异常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ConstraintViolationException.class)
    public <T> R<T> processException(ConstraintViolationException e) {
        log.error("参数校验异常: {}", ExceptionUtil.getMessage(e));
        JSONObject msg = new JSONObject();
        e.getConstraintViolations().forEach(constraintViolation -> {
            String template = constraintViolation.getMessage();
            String path = constraintViolation.getPropertyPath().toString();
            msg.set(path, template);
        });
        return R.fail(ResultCode.REQUEST_PARAM_ERROR, msg.toString());
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ValidationException.class)
    public <T> R<T> processException(ValidationException e) {
        log.error("参数校验失败: {}", ExceptionUtil.getMessage(e));
        return R.fail(ResultCode.REQUEST_PARAM_ERROR, e.getMessage());
    }

    /**
     * MissingServletRequestParameterException
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({MissingServletRequestParameterException.class,
            MethodArgumentTypeMismatchException.class,
            IllegalArgumentException.class,
            JsonProcessingException.class,
            TypeMismatchException.class
    })
    public <T> R<T> processException(Exception e) {
        log.error("输入参数错误: {}", ExceptionUtil.getMessage(e));
        return multiEnvResponseProcess(e, ResultCode.REQUEST_PARAM_ERROR);
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public <T> R<T> processException(HttpRequestMethodNotSupportedException e) {
        log.error("请求方法不支持: {}", ExceptionUtil.getMessage(e));
        return R.fail(ResultCode.REQUEST_PARAM_ERROR, "请求方法不支持");
    }

    /**
     * 请求体读取错误！
     * HttpMessageNotReadableException
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public <T> R<T> processException(HttpMessageNotReadableException e) {
        String errorMessage = "请求体不可为空";
        Throwable cause = e.getCause();
        if (cause != null) {
            errorMessage = convertMessage(cause);
        }
        log.error("请求参数错误！错误信息： {}", errorMessage);
        return R.fail(ResultCode.REQUEST_PARAM_ERROR,errorMessage);
    }

    /**
     * 401  UNAUTHORIZED
     * TokenParseException
     */
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler({TokenParseException.class})
    public R<String> processException(TokenParseException e) {
        log.error("令牌解析异常！ {}", ExceptionUtil.getMessage(e));
        return R.fail(ResultCode.TOKEN_EXPIRED);
    }

    /**
     * 404 -NoHandlerFoundException
     */
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(NoHandlerFoundException.class)
    public <T> R<T> processException(NoHandlerFoundException e) {
        log.error("请求资源不存在：{}", ExceptionUtil.getMessage(e));
        return R.fail(ResultCode.RESOURCE_NOT_FOUND);
    }

    /*
     *  业务类异常---500
     */

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(BusinessException.class)
    public <T> R<T> handleBizException(BusinessException e) {
        log.error("业务异常，异常原因：{}", ExceptionUtil.getMessage(e));
        return R.fail(ResultCode.SYSTEM_ERROR, e.getMessage());
    }

    /*
     * 非业务异常或其他异常 处理入口---500
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    public R handleException(Exception e) {
        log.error("未知异常! {}", ExceptionUtil.getMessage(e));
        return multiEnvResponseProcess(e, ResultCode.SYSTEM_ERROR);
    }


    /**
     * 传参类型错误时，用于消息转换
     *
     * @param throwable 异常
     * @return 错误信息
     */
    private String convertMessage(Throwable throwable) {
        String error = throwable.toString();
        String regulation = "\\[\"(.*?)\"]+";
        Pattern pattern = Pattern.compile(regulation);
        Matcher matcher = pattern.matcher(error);
        String group = "";
        if (matcher.find()) {
            String matchString = matcher.group();
            matchString = matchString
                    .replace("[", "")
                    .replace("]", "");
            matchString = matchString.replaceAll("\\\"", "") + "字段类型错误";
            group += matchString;
        }
        return group;
    }

    /**
     * 多环境异常处理，根据异常信息和堆栈自定义响应内容。
     *
     * @param e          异常
     * @param resultCode 响应码
     * @return
     */
    private R multiEnvResponseProcess(Exception e, ResultCode resultCode) {
        String expMsg = ExceptionUtil.getMessage(e);
        String className = e.getStackTrace()[0].getClassName();
        Integer lineNumber = e.getStackTrace()[0].getLineNumber();
        String msg = StrUtil.format("异常信息:{},错误代码: {}#第{}行", expMsg, className, lineNumber);
        //打印详细堆栈
        log.error(msg, e);
        //开发测试环境提示前端具体报错
        if (EnvConstants.DEVELOPMENT.equals(env) || EnvConstants.TEST.equals(env)) {
            return R.fail(resultCode, resultCode.getMsg() + "-" + msg);
        } else {
            return R.fail(resultCode);
        }
    }
}
